﻿/*
   Copyright 2012 University of Southampton

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace uk.ac.soton.ses
{
    public enum PathType
    {
        Unknown,
        File,
        Folder
    }

    public class HDCPath
    {
        //Example of how the path is processed:
        //  Given a path of C:\HDC\PARENTFOLDER\SUBFOLDER\FILE
        //  and a watched folder of C:\HDC,
        //  basePath would be C:\HDC\PARENTFOLDER,
        //  remainingPath would be SUBFOLDER\FILE
        //  pathRelativeToWatchedFolder would be PARENTFOLDER\SUBFOLDER\FILE
        //  topLevelFolderName would be PARENTFOLDER
        //  objectName would be FILE
        //  objectPath would be C:\MDC\PARENTFOLDER\SUBFOLDER


        //Fields
        private int _indexOfSeparatorAfterBasePath = -1;
        private int _indexOfSeparatorBeforeBasePath = -1;
        private int _indexOfSeparatorAfterWatchedFolder = -1;
        private int _basePathDepthInWatchFolder = 0;
        private string _watchFolder = "";
        private string _basePath = "";
        private string _remainingPath = "";
        private string _pathRelativeToWatchedFolder = "";
        private string _topLevelFolderName = "";
        private string _fullPath = "";
        private string _objectName = "";
        private string _objectPath = "";
        private PathType _pathType = PathType.Unknown;

        //Properties
        public int IndexOfBasePathSeparator { get { return this._indexOfSeparatorAfterBasePath; } }
        public int BasePathDepthInWatchFolder { get { return this._basePathDepthInWatchFolder; } }
        public string BasePath { get { return this._basePath; } }
        public string RemainingPath { get { return this._remainingPath; } }
        public string PathRelativeToWatchedFolder { get { return this._pathRelativeToWatchedFolder; } }
        public string TopLevelFolderName { get { return this._topLevelFolderName; } }
        public string FullPath { get { return this._fullPath; } }
        public string ObjectName { get { return this._objectName; } }
        public string ObjectPath { get { return this._objectPath; } }
        public PathType PathType { get { return this._pathType; } }
        public string WatchFolder { get { return this._watchFolder; } }

        public static PathType GetPathType(string path)
        {
            PathType pathType = PathType.Unknown;
            //Check item exists first
            if (Directory.Exists(path))
            {
                pathType = PathType.Folder;
            }
            else if (File.Exists(path))
            {
                pathType = PathType.File;
            }
            else
            {
                pathType = PathType.Unknown;
            }
            return pathType;
        }

        /// <summary>
        /// Given a watch folder, breaks a path into parts to find the base path and remaining path
        /// </summary>
        /// <param name="path">The path to analyse</param>
        /// <param name="watchFolder">The folder we are watching, to be used for finding parts of the path</param>
        /// <param name="basePathDepthInWatchFolder">The number of folders below the watch folder before we start recognising folders as experiments. Top level is 0</param>
        public HDCPath(string path, string watchFolder, int basePathDepthInWatchFolder)
        {
            this._fullPath = path;
            this._watchFolder = watchFolder;
            this._basePathDepthInWatchFolder = basePathDepthInWatchFolder;
            this._pathType = HDCPath.GetPathType(this._fullPath);

            //Find separators in the path:

            //Find the end of the watched folder and the start of the remaining path
            this._indexOfSeparatorAfterWatchedFolder = FindCharIndexOccurence(this._fullPath, Path.DirectorySeparatorChar, this._watchFolder.Length - 1, 1);

            //Start looking for a backslash at 2 characters past the last character of the watched folder, ignoring the backslash. We only add 1 to folderToWatch.Length as startIndex is zero based

            //Find the separator immediately before the base path folder (i.e., after parent)
            this._indexOfSeparatorBeforeBasePath = FindCharIndexOccurence(this._fullPath, Path.DirectorySeparatorChar, this._watchFolder.Length - 1, this._basePathDepthInWatchFolder + 1);

            //Find the separator at the end of the base path and start of remaining path.
            this._indexOfSeparatorAfterBasePath = FindCharIndexOccurence(this._fullPath, Path.DirectorySeparatorChar, this._watchFolder.Length - 1, this._basePathDepthInWatchFolder + 2);

            //Version 2, with user name
            //  Given a path of C:\HDC\USERFOLDER\PARENTFOLDER\SUBFOLDER\FILE
            //  and a watched folder of C:\HDC,
            //  basePath would be C:\HDC\USERFOLDER\PARENTFOLDER,
            //  remainingPath would be SUBFOLDER\FILE
            //  pathRelativeToWatchedFolder would be USERFOLDER\PARENTFOLDER\SUBFOLDER\FILE
            //  topLevelFolderName would be PARENTFOLDER
            //  objectName would be FILE
            //  objectPath would be C:\MDC\USERFOLDER\PARENTFOLDER\SUBFOLDER

            //There is no slash found after watched folder path, which means the activity was the watched folder itself
            if (this._indexOfSeparatorAfterWatchedFolder <= 0 || this._indexOfSeparatorBeforeBasePath <=0)
            {
                this._basePath = "";
                this._remainingPath = "";
                this._pathRelativeToWatchedFolder = "";
                this._topLevelFolderName = "";
                this._objectName = "";
                this._objectPath = "";
                return;
            }

            //At this point, indexOfBasePathSeparator is -1 if there is a base path but nothing else.
            //This could be a new experiment
            if (this._indexOfSeparatorAfterWatchedFolder > 0 && this._indexOfSeparatorBeforeBasePath > 0 && this._indexOfSeparatorAfterBasePath <= 0)
            {
                this._basePath = path; //The base path is the same as the path if it's a top level folder
                this._remainingPath = ""; //There is no remaining path when it's a top level folder
                this._pathRelativeToWatchedFolder = path.Substring(this._watchFolder.Length + 1); //Get all text starting from 1 character after the backslash
                this._topLevelFolderName = this._pathRelativeToWatchedFolder; //topLevelFolderName should be the same as pathRelativeToWatchedFolder because this is a top level folder
                //2012-08-24, removed the following line as it seems pointless as it will be overwritten by the line after
                //this._objectName = new FileInfo(this._fullPath).Name;// .Remove(0, folderToWatch.Length);
                this._objectName = this._fullPath.Substring(this._fullPath.LastIndexOf(Path.DirectorySeparatorChar) + 1);
                this._objectPath = this._fullPath.Substring(0, this._fullPath.LastIndexOf(Path.DirectorySeparatorChar));
                return;
            }

            if (this._indexOfSeparatorAfterWatchedFolder > 0 && this._indexOfSeparatorAfterBasePath > 0)
            {
                this._basePath = path.Substring(0, _indexOfSeparatorAfterBasePath); //Store the base path, stopping before the backslash
                this._remainingPath = path.Substring(_indexOfSeparatorAfterBasePath + 1); //Store the remaining text (add one because we don't want to store the backslash)
                this._pathRelativeToWatchedFolder = path.Substring(this._watchFolder.Length + 1); //Get the relative path of the file in relation to the watched folder
                this._topLevelFolderName = this._pathRelativeToWatchedFolder.Substring(0, this._pathRelativeToWatchedFolder.IndexOf(Path.DirectorySeparatorChar)); //We know there's a backslash because it's the same one as we found earlier
                this._objectName = this._fullPath.Substring(this._fullPath.LastIndexOf(Path.DirectorySeparatorChar) + 1);
                this._objectPath = this._fullPath.Substring(0, this._fullPath.LastIndexOf(Path.DirectorySeparatorChar));
                return;
            }
        }

        /// <summary>
        /// Find the Nth occurrence of a character in a string
        /// </summary>
        /// <param name="s">The string to search</param>
        /// <param name="c">The character to find</param>
        /// <param name="startindex">The index to start the search</param>
        /// <param name="occurrence">Which occurence to find</param>
        /// <returns>The character index of the Nth occurrence of a character in a string</returns>
        private static int FindCharIndexOccurence(string s, char c, int startindex, int occurrence)
        {
            int position = s.IndexOf(c, startindex);
            for (int i = 2; i <= occurrence && position != -1; i++)
            {
                position = s.IndexOf(c, position + 1);
            }
            return position;
        }
    }
}
